<?php
namespace App\Common;

use PhalApi\Filter;
use PhalApi\Exception\BadRequestException;
use App\Model\AppModel;

/**
 * @name 拦截器
 * @author CabbageRun 2017-11-15
 *
 */

class MyFilter implements Filter
{

    protected $signName;

    public function __construct($signName = 'Sign')
    {
        //打印post提交的数据
        /* $allParams = \PhalApi\DI()->request->getAll();
        var_dump($allParams);die; */
        $this->signName = $signName;
    }

    /**
     * 系统检测
     * {@inheritDoc}
     * @see \PhalApi\Filter::check()
     * @desc 检测AppKey/Sign/TimeStamp
     */
    public function check()
    {
        $allParams = \PhalApi\DI()->request->getAll();
        if(! isset($allParams['AppKey']) || empty($allParams['AppKey'])){
            throw new BadRequestException(\PhalApi\T('缺少必要参数AppKey！'), 6);
        }
        //排除获取签名接口
        $action = \PhalApi\DI()->request->getServiceAction();
        $action = strtolower($action);
        if($action != 'getsign' && $action != 'appdowload'){
            if(! isset($allParams[$this->signName]) || empty($allParams[$this->signName])){
                throw new BadRequestException(\PhalApi\T('缺少必要参数'.$this->signName.'！'), 6);
            }
            $sign = $allParams[$this->signName];
            if(! isset($allParams['TimeStamp']) || empty($allParams['TimeStamp'])){
                throw new BadRequestException(\PhalApi\T('缺少必要参数TimeStamp！'), 6);
            }
            if($allParams['TimeStamp'] < time()){//TimeStamp为当前时间加30秒，30秒后过期
                throw new BadRequestException(\PhalApi\T('App项目签名过期！'), 6);
            }
            $expectSign = $this->getSign($allParams);
            //app项目签名不符
            if($expectSign != $sign) {
                \PhalApi\DI()->logger->log('error', '签名错误', array('Sign' => $expectSign));
                throw new BadRequestException(\PhalApi\T('App项目签名错误！'), 6);
            }
        }
    }

    /**
     * 获取签名
     * @param unknown $params
     * @throws BadRequestException
     * @desc 在app项目存在的情况下才获取Sign
     */
    protected function getSign($params)
    {
        $where = array();
        $domain = new AppModel();
        $field = 'id,appkey,securitykey,appstate';
        $where['appkey = ?'] = $params['AppKey'];
        $row = $domain->db_get($field, $where, 'row');
        if(! $row){
            throw new BadRequestException(\PhalApi\T('App项目不存在！'), 6);
        }
        if(! isset($row['appstate']) || $row['appstate'] == 0){
            throw new BadRequestException(\PhalApi\T('App项目已经关闭！'), 6);
        }
        $sign = $this->encrypt($params, $row['securitykey']);
        return $sign;
    }

    /**
     * 生成签名
     * @param unknown $params
     * @param unknown $s_key
     * @desc 规则:
     * 1.对除签名外的所有请求参数按key做的升序排列,value无需编码。
     * 例如：有c=3,b=2,a=1 三个参，另加上时间戳后， 按key升序排序后为：a=1，b=2，c=3，timestamp=12345678。
     * 2 把参数名和参数值连接成字符串，得到拼装字符：a1b2c3timestamp12345678
     * 3 用服务端发放的SecurityKey(安全码)连接到接拼装字符串头部和尾部，然后进行32位MD5加密，最后将到得MD5加密摘要转化成大写。
     * 示例：假设SecurityKey=test，md5(testa1b2c3timestamp12345678test)，生成的字符串即为Sign(签名)
     */
    protected function encrypt($params, $s_key)
    {
        unset($params[$this->signName]);//排除Sign
        ksort($params);
        $encrypt_str = '';
        foreach($params as $key => $value){
            $encrypt_str .= $key.$value;
        }
        $encrypt_str = $s_key.$encrypt_str.$s_key;
        return md5($encrypt_str);
    }
}
